
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "abb_avl.h"

/* ******************************************************************
 *                      FUNCIONES AUXILIARES
 * *****************************************************************/

/* Función auxiliar para imprimir si estuvo OK o no. */
void print_test(char* name, bool result)
{
    printf("%s: %s\n", name, result? "OK" : "ERROR");
}


/* ******************************************************************
 *                        PRUEBAS UNITARIAS
 * *****************************************************************/


/* Prueba que las primitivas de la lista funcionen correctamente. */
void prueba_crear_abb_vacio(){
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	print_test("Prueba abb crear abb vacio", abb);
	print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);
	print_test("Prueba abb obtener clave A, es NULL, no existe", !avl_obtener(abb, "A"));
	print_test("Prueba abb pertenece clave A, es false, no existe", !avl_pertenece(abb, "A"));
	print_test("Prueba abb borrar clave A, es NULL, no existe", !avl_borrar(abb, "A"));

	avl_destruir(abb);
}

void prueba_iterar_abb_vacio(){
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	avl_iter_t* iter = avl_iter_in_crear(abb);
	print_test("Prueba abb iter crear iterador abb vacio", iter);
	print_test("Prueba abb iter esta al final", avl_iter_in_al_final(iter));
	print_test("Prueba abb iter avanzar es false", !avl_iter_in_avanzar(iter));
	print_test("Prueba abb iter ver actual es NULL", !avl_iter_in_ver_actual(iter));

	avl_iter_in_destruir(iter);
	avl_destruir(abb);
}

void prueba_abb_insertar(){
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *clave1 = "perro", *valor1 = "guau";
	char *clave2 = "gato", *valor2 = "miau";
	char *clave3 = "vaca", *valor3 = "mu";

	/* Inserta 1 valor y luego lo borra */
	print_test("Prueba abb insertar clave1", avl_guardar(abb, clave1, valor1));
	print_test("Prueba abb la cantidad de elementos es 1", avl_cantidad(abb) == 1);
	print_test("Prueba abb obtener clave1 es valor1", avl_obtener(abb, clave1) == valor1);
	print_test("Prueba abb obtener clave1 es valor1", avl_obtener(abb, clave1) == valor1);
	print_test("Prueba abb pertenece clave1, es true", avl_pertenece(abb, clave1));
	print_test("Prueba abb borrar clave1, es valor1", avl_borrar(abb, clave1) == valor1);
	print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);

	/* Inserta otros 2 valores y no los borra (se destruyen con el abb) */
	print_test("Prueba abb insertar clave2", avl_guardar(abb, clave2, valor2));
	print_test("Prueba abb la cantidad de elementos es 1", avl_cantidad(abb) == 1);
	print_test("Prueba abb obtener clave2 es valor2", avl_obtener(abb, clave2) == valor2);
	print_test("Prueba abb obtener clave2 es valor2", avl_obtener(abb, clave2) == valor2);
	print_test("Prueba abb pertenece clave2, es true", avl_pertenece(abb, clave2));

	print_test("Prueba abb insertar clave3", avl_guardar(abb, clave3, valor3));
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);
	print_test("Prueba abb obtener clave3 es valor3", avl_obtener(abb, clave3) == valor3);
	print_test("Prueba abb obtener clave3 es valor3", avl_obtener(abb, clave3) == valor3);
	print_test("Prueba abb pertenece clave3, es true", avl_pertenece(abb, clave3)); 

	avl_destruir(abb);
}

void prueba_abb_reemplazar(){
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *clave1 = "perro", *valor1a = "guau", *valor1b = "warf";
	char *clave2 = "gato", *valor2a = "miau", *valor2b = "meaow";

	/* Inserta 2 valores y luego los reemplaza */
	print_test("Prueba abb insertar clave1", avl_guardar(abb, clave1, valor1a));
	print_test("Prueba abb obtener clave1 es valor1a", avl_obtener(abb, clave1) == valor1a);
	print_test("Prueba abb obtener clave1 es valor1a", avl_obtener(abb, clave1) == valor1a);
	print_test("Prueba abb insertar clave2", avl_guardar(abb, clave2, valor2a));
	print_test("Prueba abb obtener clave2 es valor2a", avl_obtener(abb, clave2) == valor2a);
	print_test("Prueba abb obtener clave2 es valor2a", avl_obtener(abb, clave2) == valor2a);
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);

	print_test("Prueba abb insertar clave1 con otro valor", avl_guardar(abb, clave1, valor1b));
	print_test("Prueba abb obtener clave1 es valor1b", avl_obtener(abb, clave1) == valor1b);
	print_test("Prueba abb obtener clave1 es valor1b", avl_obtener(abb, clave1) == valor1b);
	print_test("Prueba abb insertar clave2 con otro valor", avl_guardar(abb, clave2, valor2b));
	print_test("Prueba abb obtener clave2 es valor2b", avl_obtener(abb, clave2) == valor2b);
	print_test("Prueba abb obtener clave2 es valor2b", avl_obtener(abb, clave2) == valor2b);
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);

	avl_destruir(abb);
}

void prueba_abb_reemplazar_con_destruir()
{
	abb_avl_t* abb = avl_crear(strcmp,free);
	
	char *clave1 = "perro", *valor1a, *valor1b;
	char *clave2 = "gato", *valor2a, *valor2b;

	/* Pide memoria para 4 valores */
	valor1a = malloc(10 * sizeof(char));
	valor1b = malloc(10 * sizeof(char));
	valor2a = malloc(10 * sizeof(char));
	valor2b = malloc(10 * sizeof(char));

	/* Inserta 2 valores y luego los reemplaza (debe liberar lo que reemplaza) */
	print_test("Prueba abb insertar clave1", avl_guardar(abb, clave1, valor1a));
	print_test("Prueba abb obtener clave1 es valor1a", avl_obtener(abb, clave1) == valor1a);
	print_test("Prueba abb obtener clave1 es valor1a", avl_obtener(abb, clave1) == valor1a);
	print_test("Prueba abb insertar clave2", avl_guardar(abb, clave2, valor2a));
	print_test("Prueba abb obtener clave2 es valor2a", avl_obtener(abb, clave2) == valor2a);
	print_test("Prueba abb obtener clave2 es valor2a", avl_obtener(abb, clave2) == valor2a);
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);

	print_test("Prueba abb insertar clave1 con otro valor", avl_guardar(abb, clave1, valor1b));
	print_test("Prueba abb obtener clave1 es valor1b", avl_obtener(abb, clave1) == valor1b);
	print_test("Prueba abb obtener clave1 es valor1b", avl_obtener(abb, clave1) == valor1b);
	print_test("Prueba abb insertar clave2 con otro valor", avl_guardar(abb, clave2, valor2b));
	print_test("Prueba abb obtener clave2 es valor2b", avl_obtener(abb, clave2) == valor2b);
	print_test("Prueba abb obtener clave2 es valor2b", avl_obtener(abb, clave2) == valor2b);
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);

	/* Se destruye el abb (se debe liberar lo que quedó dentro) */
	avl_destruir(abb);
}

void prueba_abb_borrar()
{
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *clave1 = "perro", *valor1 = "guau";
	char *clave2 = "gato", *valor2 = "miau";
	char *clave3 = "vaca", *valor3 = "mu";

	/* Inserta 3 valores y luego los borra */
	print_test("Prueba abb insertar clave1", avl_guardar(abb, clave1, valor1));
	print_test("Prueba abb insertar clave2", avl_guardar(abb, clave2, valor2));
	print_test("Prueba abb insertar clave3", avl_guardar(abb, clave3, valor3));

	/* Al borrar cada elemento comprueba que ya no está pero los otros sí. */
	print_test("Prueba abb pertenece clave3, es verdadero", avl_pertenece(abb, clave3));
	print_test("Prueba abb borrar clave3, es valor3", avl_borrar(abb, clave3) == valor3);
	print_test("Prueba abb borrar clave3, es NULL", !avl_borrar(abb, clave3));
	print_test("Prueba abb pertenece clave3, es falso", !avl_pertenece(abb, clave3));
	print_test("Prueba abb obtener clave3, es NULL", !avl_obtener(abb, clave3));
	print_test("Prueba abb la cantidad de elementos es 2", avl_cantidad(abb) == 2);

	print_test("Prueba abb pertenece clave1, es verdadero", avl_pertenece(abb, clave1));
	print_test("Prueba abb borrar clave1, es valor1", avl_borrar(abb, clave1) == valor1);
	print_test("Prueba abb borrar clave1, es NULL", !avl_borrar(abb, clave3));
	print_test("Prueba abb pertenece clave1, es falso", !avl_pertenece(abb, clave1));
	print_test("Prueba abb obtener clave1, es NULL", !avl_obtener(abb, clave1));
	print_test("Prueba abb la cantidad de elementos es 1", avl_cantidad(abb) == 1);

	print_test("Prueba abb pertenece clave2, es verdadero", avl_pertenece(abb, clave2));
	print_test("Prueba abb borrar clave2, es valor2", avl_borrar(abb, clave2) == valor2);
	print_test("Prueba abb borrar clave2, es NULL", !avl_borrar(abb, clave3));
	print_test("Prueba abb pertenece clave2, es falso", !avl_pertenece(abb, clave2));
	print_test("Prueba abb obtener clave2, es NULL", !avl_obtener(abb, clave2));
	print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);

	avl_destruir(abb);
}

void prueba_abb_clave_vacia()
{
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *clave = "", *valor = "";

	print_test("Prueba abb insertar clave vacia", avl_guardar(abb, clave, valor));
	print_test("Prueba abb la cantidad de elementos es 1", avl_cantidad(abb) == 1);
	print_test("Prueba abb obtener clave vacia es valor", avl_obtener(abb, clave) == valor);
	print_test("Prueba abb pertenece clave vacia, es true", avl_pertenece(abb, clave));
	print_test("Prueba abb borrar clave vacia, es valor", avl_borrar(abb, clave) == valor);
	print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);

	avl_destruir(abb);
}

void prueba_abb_valor_null()
{
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *clave = "", *valor = NULL;

	/* Inserta 1 valor y luego lo borra */
	print_test("Prueba abb insertar clave vacia valor NULL", avl_guardar(abb, clave, valor));
	print_test("Prueba abb la cantidad de elementos es 1", avl_cantidad(abb) == 1);
	print_test("Prueba abb obtener clave vacia es valor NULL", avl_obtener(abb, clave) == valor);
	print_test("Prueba abb pertenece clave vacia, es true", avl_pertenece(abb, clave));
	print_test("Prueba abb borrar clave vacia, es valor NULL", avl_borrar(abb, clave) == valor);
	print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);

	avl_destruir(abb);
}

void swap (char** elem1, char** elem2){
	char* aux = *elem1;
	*elem1 = *elem2;
	*elem2 = aux;

}

void prueba_abb_volumen(size_t largo, bool debug) 
{
	abb_avl_t* abb = avl_crear(strcmp,NULL);

	const size_t largo_clave = 10;
	char** claves = malloc(largo * sizeof(char*));

	int* valores[largo];
	
	/* Genero un vector de claves ordenado */
	for (int i = 0; i < largo; i++) {
		valores[i] = malloc(sizeof(int));
		claves[i] = malloc (largo_clave*sizeof(char));	
		sprintf(claves[i], "%08d", i);
		*valores[i] = i;
	}
	
	for (int i = largo-1; i>0; i--){
		int rand_pos = rand() % i;
		swap(&claves[rand_pos],&claves[i]);
		
	}
	
	struct timeval startTime, endTime;
	if (!debug)
	 	gettimeofday (&startTime,NULL);
	
	
	/* Inserta 'largo' parejas en el abb */
	bool ok = true;
	for (int i = 0; i < largo; i++) {
		ok = avl_guardar(abb, claves[i], valores[i]);
		if (!ok) break;
	}

		
	if (debug) print_test("Prueba abb almacenar muchos elementos", ok);
	if (debug) print_test("Prueba abb la cantidad de elementos es correcta", avl_cantidad(abb) == largo);


	/* Verifica que devuelva los valores correctos */	
	for (size_t i = 0; i < largo; i++) {
		ok = avl_pertenece(abb, claves[i]);
		if (!ok) break;
		ok = avl_obtener(abb, claves[i]) == valores[i];
		if (!ok) break;
	}

	if (debug) print_test("Prueba abb pertenece y obtener muchos elementos", ok);
	if (debug) print_test("Prueba abb la cantidad de elementos es correcta", avl_cantidad(abb) == largo);


	/* Verifica que borre y devuelva los valores correctos */
	for (size_t i = 0; i < largo; i++) {
		ok = avl_borrar(abb, claves[i]) == valores[i];
		if (!ok) break;
	} 

	if (debug) print_test("Prueba abb borrar muchos elementos", ok);
	if (debug) print_test("Prueba abb la cantidad de elementos es 0", avl_cantidad(abb) == 0);

	/* Destruye el abb y crea uno nuevo que sí libera */
	avl_destruir(abb);
	abb = avl_crear(strcmp,free);

	/* Inserta 'largo' parejas en el abb */
	ok = true;
	for (size_t i = 0; i < largo; i++) {
		ok = avl_guardar(abb, claves[i], valores[i]);
		if (!ok) break;
	} 
	
	
	for (int i = 0; i<largo; i++){
		free (claves[i]);
	}
	
	free(claves);
	
	
	
	/* Destruye el abb - debería liberar los enteros */
	avl_destruir(abb); 
	
	if (!debug){
		gettimeofday (&endTime,NULL);
		double tS = startTime.tv_sec*1000000 + (startTime.tv_usec);
		double tE = endTime.tv_sec*1000000 + (endTime.tv_usec);
		double t = (tE-tS)/1000000;
	
		printf ("Tiempo: %f seg\n",t);
	}
		

}

int buscar(const char* clave, char* claves[], size_t largo) 
{
	for (size_t i = 0; i < largo; i++) {
		if (strcmp(clave, claves[i]) == 0) return i;
	}
	return -1;
}

void prueba_abb_iterar()
{
	abb_avl_t* abb = avl_crear(strcmp,NULL);
	
	char *claves[] = {"perro", "gato", "vaca"};
	char *valores[] = {"guau", "miau", "mu"};

	/* Inserta 3 valores */
	print_test("Prueba abb insertar clave1", avl_guardar(abb, claves[0], valores[0]));
	print_test("Prueba abb insertar clave2", avl_guardar(abb, claves[1], valores[1]));
	print_test("Prueba abb insertar clave3", avl_guardar(abb, claves[2], valores[2]));

	// Prueba de iteración sobre las claves almacenadas.
	avl_iter_t* iter = avl_iter_in_crear(abb);
	const char *clave;
	int indice;
	
	print_test("Prueba abb iterador esta al final, es false", !avl_iter_in_al_final(iter));

	/* Primer valor */
	clave = avl_iter_in_ver_actual(iter);
	indice = buscar(clave, claves, sizeof(claves));
	print_test("Prueba abb iterador ver actual, es una clave valida", indice != -1);
	print_test("Prueba abb iterador ver actual, no es el mismo puntero", clave != claves[indice]);
	print_test("Prueba abb iterador avanzar es true", avl_iter_in_avanzar(iter));
	print_test("Prueba abb iterador esta al final, es false", !avl_iter_in_al_final(iter));

	/* Segundo valor */
	clave = avl_iter_in_ver_actual(iter);
	indice = buscar(clave, claves, sizeof(claves));
	print_test("Prueba abb iterador ver actual, es una clave valida", indice != -1);
	print_test("Prueba abb iterador ver actual, no es el mismo puntero", clave != claves[indice]);
	print_test("Prueba abb iterador avanzar es true", avl_iter_in_avanzar(iter));
	print_test("Prueba abb iterador esta al final, es false", !avl_iter_in_al_final(iter));

	/* Tercer valor */
	clave = avl_iter_in_ver_actual(iter);
	indice = buscar(clave, claves, sizeof(claves));
	print_test("Prueba abb iterador ver actual, es una clave valida", indice != -1);
	print_test("Prueba abb iterador ver actual, no es el mismo puntero", clave != claves[indice]);
	/* Se anula esta prueba por diferencias de criterios */	
	avl_iter_in_avanzar(iter);
	print_test("Prueba abb iterador esta al final, es true", avl_iter_in_al_final(iter));

	/* Vuelve a tratar de avanzar, por las dudas */
	print_test("Prueba abb iterador ver actual, es NULL", !avl_iter_in_ver_actual(iter));
	print_test("Prueba abb iterador avanzar es false", !avl_iter_in_avanzar(iter));
	print_test("Prueba abb iterador esta al final, es true", avl_iter_in_al_final(iter));

	avl_iter_in_destruir(iter);
	avl_destruir(abb);
	
}

/* ******************************************************************
 *                        PROGRAMA PRINCIPAL
 * *****************************************************************/

/* Programa principal. */
int main(int argc, char** argv)
{

	if (argc < 2) {
	    /* Ejecuta todas las pruebas unitarias. */
		prueba_crear_abb_vacio();
		prueba_iterar_abb_vacio();
		prueba_abb_insertar();
		prueba_abb_reemplazar();
		prueba_abb_reemplazar_con_destruir();
		prueba_abb_borrar();
		prueba_abb_volumen(5000, true);
		prueba_abb_iterar();
	} else {
		size_t largo = atoi(argv[1]);
		
		prueba_abb_volumen(largo, false);
		
	
		
	}
    return 0;
}

